/** @file oal_main.h
  *
  * @brief This file contains oal driver specific defines etc.
  *
  *  Copyright (C) 2024, Artosyn Technologies. ALL RIGHTS RESERVED.
  *
  *  This Information is proprietary to Artosyn Technologies and MAY NOT
  *  be copied by any method or incorporated into another program without
  *  the express written consent of Artosyn. This Information or any portion
  *  thereof remains the property of Artosyn. The Information contained herein
  *  is believed to be accurate and Artosyn assumes no responsibility or
  *  liability for its use in any way and conveys no license or title under
  *  any patent or copyright and makes no representation or warranty that this
  *  Information is free from patent or copyright infringement.
  *
  */

/********************************************************
Change log:
    10/21/2024: initial version
********************************************************/

#ifndef _OAL_MAIN_H
#define _OAL_MAIN_H

/* Linux header files */
#include        <linux/kernel.h>
#include        <linux/module.h>
#include        <linux/init.h>
#include        <linux/version.h>
#include        <linux/param.h>
#include        <linux/delay.h>
#include        <linux/slab.h>
#include        <linux/mm.h>
#include        <linux/types.h>
#include        <linux/sched.h>
#include        <linux/timer.h>
#include        <linux/ioport.h>
#include        <linux/ctype.h>
#ifdef ENABLE_PROC_FS
#include        <linux/proc_fs.h>
#endif
#include        <linux/vmalloc.h>
#include        <linux/ptrace.h>
#include        <linux/string.h>
#include        <linux/irqreturn.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
#include        <linux/namei.h>
#include        <linux/fs.h>
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
#include       <linux/config.h>
#endif

/* ASM files */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
#include        <linux/semaphore.h>
#else
#include        <asm/semaphore.h>
#endif
#include        <asm/byteorder.h>
#include        <asm/irq.h>
#include        <asm/uaccess.h>
#include        <asm/io.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
#include        <asm/switch_to.h>
#else
#include        <asm/system.h>
#endif

/* Net header files */
#include        <linux/netdevice.h>
#include        <linux/net.h>
#include        <linux/inet.h>
#include        <linux/ip.h>
#include        <linux/skbuff.h>
#include        <linux/if_ether.h>
#include        <linux/etherdevice.h>
#include        <net/sock.h>

#include        "control/ar_chardev.h"
#include        "control/ar_proc.h"
#include        "rpc/ar_rpc.h"
#include        "utils/utils.h"

#define AR_DRV_DESCRIPTION  "Artosyn 8030 driver for Linux"
#define AR_DRV_COPYRIGHT    "Copyright(c) 2024-2025 Artosyn"
#define AR_DRV_AUTHOR       "Artosyn"
#define AR_VERS_MOD         "0.1.0.3"

/**
 * the maximum number of adapter supported
 **/
#define MAX_OAL_ADAPTER    16

/** Handle data structure for oal  */
typedef struct _oal_handle oal_handle;

struct ar_skbreq;

/** Message queue size */
#define OAL_MSGOUT_NUM     100
#define OAL_MSGIN_NUM      150

/**
 * receive msg and data by the same pkt queue buffer
*/
#define OAL_PKTIN_NUM       100
#define OAL_PTKOUT_NUM      300

/** MTU */
#define OAL_MTU_PKT_I       (20 * 1024)    /*ar8030 may send pkt bundle by bb*/

/** oal req type */
typedef enum _oal_req_type {
    OAL_REQ_TYPE_PKTIN,
    OAL_REQ_TYPE_PKTOUT,
    OAL_REQ_TYPE_MSGOUT,
    OAL_REQ_TYPE_MAX
} oal_req_type;

/** oal_status */
typedef enum _oal_status {
    OAL_STATUS_FAILURE = 0xffffffff,
    OAL_STATUS_SUCCESS = 0,
    OAL_STATUS_PENDING,
    OAL_STATUS_RESOURCE,
    OAL_STATUS_COMPLETE,
} oal_status;

/** oal_card_mode */
typedef enum _oal_card_mode {
    OAL_CARD_MODE_FW_DNLD = 0,     /* card for fw download */
    OAL_CARD_MODE_RPC,             /* card for artosyn rpc */
    OAL_CARD_MODE_MAX
} oal_card_mode;

/** skbreq list type */
typedef enum _skbreq_list_type {
    SKBREQ_LIST_MSGOUT_FREE = 1,
    SKBREQ_LIST_MSGOUT_POST,
    SKBREQ_LIST_PKTIN_FREE,
    SKBREQ_LIST_PKTIN_PEND,
    SKBREQ_LIST_PKTIN_POST,
    SKBREQ_LIST_PKTOUT_FREE,
    SKBREQ_LIST_PKTOUT_POST
} skbreq_list_type;

/** oal_callbacks data structure */
typedef struct _oal_callbacks {
    /** oal_update_card_type */
    void (*oal_update_card_type)(IN oal_handle *handle, void *card);

    /** oal_alloc_skbreq_ex */
    oal_status (*oal_alloc_skbreq_ex)(IN oal_handle *handle, IN struct ar_skbreq *req);

    /** oal_send_dnld_req */
    oal_status (*oal_send_dnld_req)(IN oal_handle *handle, u32 addr, u8* data, u32 len, u16 timeout);
    /** oal_send_dnld_done */
    oal_status (*oal_send_dnld_done)(IN oal_handle *handle, u16 timeout);

    oal_status (*oal_register_dev)(oal_handle *handle);
    void (*oal_unregister_dev)(oal_handle *handle);

    /** Push the pkt in req to pend list(for usb now) */
    void (*oal_try_recv_pend) (IN oal_handle* handle);

    /** oal_recv_packet_complete */
    oal_status (*oal_recv_packet_complete)(IN oal_handle *poal_handle,
                                        IN struct ar_skbreq *req,
                                        IN oal_status status);

    /** oal_write_reg */
    oal_status (*oal_write_reg) (IN oal_handle *poal_handle,
                       IN u32 reg, IN u32 data);
    /** oal_read_reg */
    oal_status (*oal_read_reg) (IN oal_handle *poal_handle,
                      IN u32 reg, OUT u32 *data);
    /** oal_write_data_sync */
    oal_status (*oal_write_data_sync) (IN oal_handle *poal_handle,
                         IN struct ar_skbreq *req);
    /** oal_write_data_async */
    oal_status (*oal_write_data_async) (IN oal_handle *poal_handle,
                         IN struct ar_skbreq *req);
    /** oal_read_data_sync */
    oal_status (*oal_read_data_sync) (IN oal_handle *poal_handle,
                        IN struct ar_skbreq *req);
    /** oal_read_data_async */
    oal_status (*oal_read_data_async) (IN oal_handle *poal_handle,
                        IN struct ar_skbreq *req);
} oal_callbacks, *poal_callbacks;

/*Bus state related structure*/
enum ar_bus_state {
    AR_BUS_STATE_DOWN,
    AR_BUS_STATE_STOP,
    AR_BUS_STATE_DL_FAIL,
    AR_BUS_STATE_DL_DONE,
    AR_BUS_STATE_UP,
    AR_BUS_STATE_SLEEP
};

/** Handle data structure for OAL */
struct _oal_handle {
    void *oal_dev;

    struct device *dev;
    void *ar_rpc;
    void *net_dev;

    /** Artosyn device */
    ar_dev_t ar_dev;
    /** proc data */
    struct device_proc dev_proc;

    //lock for ar rpc
    spinlock_t ar_rpc_lock;
    /* ar rpc ref cnt for deinit */
    atomic_t ar_rpc_ref_cnt;

    //queue resource
    u32 pktin_mtu;

    spinlock_t qlock;
    spinlock_t tx_qlock;

    /* msg out */
    struct list_head msgout_freeq;
    struct list_head msgout_postq;

    /* pkt in */
    struct list_head pktin_freeq;
    struct list_head pktin_pendq;
    struct list_head pktin_postq;

    /* pkt out */
    struct list_head pktout_freeq;
    struct list_head pktout_postq;

    int pktin_freecount;
    int pktout_freecount;
    int msgout_freecount;
    /* pkt in pending count */
    int pktin_pendcount;
    int pktin_postcount;
    /* msg out pending count for tx limit */
    atomic_t msgout_cnt;

    struct ar_skbreq *pktout_reqs;
    struct ar_skbreq *pktin_reqs;
    struct ar_skbreq *pktin_pend_reqs;

    struct ar_skbreq *msgout_reqs;
    // struct usb_anchor msgout_anchor;

    //firmware resource
    const struct firmware *firmware;
    const struct firmware *cfg;

    //status, sync
    spinlock_t state_lock;
    enum ar_bus_state state;
    bool b_disconnecting;

    struct completion dev_init_lock;

    //TBD:power management
#if 0
    struct completion resume_wait;
    bool bb_suspend;
    ktime_t cmd_start_time;

    /* controller suspend count*/
    u32 suspend_d_cnt;
    /* host suspend count*/
    u32 suspend_s_cnt;
    /* host reset resume count*/
    u32 reset_resume_cnt;
    /* host auto pm start time */
    ktime_t system_start_time;
    /* host last suspend start time*/
    ktime_t suspend_start_time;
    /* host suspend totoal time */
    ktime_t suspend_ttl_time;
#endif

    /** Callbacks */
    oal_callbacks callbacks;

    /** Card pointer */
    void *card;
    /** Card type */
    u16 card_type;
    /** handle index - for multiple card supports */
    u8 handle_idx;
    /* mode of card for different funcs */
    oal_card_mode card_mode;

    /** add/remove card sem */
    struct semaphore addRemoveCardSem;

    struct class *chardev_class;
    u8 chardev_class_name[AR_MODULE_NAME_LEN];

    /* m_dev structure */
    u8 *net_mdev_name;
    struct m_dev net_mdev;
    struct m_dev ar_drv_mdev;

    /** net dev param */
    /** Spin lock for net device operation */
    spinlock_t ar_net_dev_lock;
    /** net dev opr lock */
    spinlock_t net_dev_opr_lock;
    /** net dev opr ref cnt */
    atomic_t net_dev_opr_ref_cnt;
    /** List of the net device */
    struct list_head ar_net_dev_list;
    /** Status of the net device */
    bool net_dev_inited;

    /** tx workqueue */
    void *main_workqueue;     /** refers to oal_workqueue */
    /** rx workqueue */
    void *rx_workqueue;     /** refers to oal_workqueue */
    /** tx workqueue */
    void *tx_workqueue;     /** refers to oal_workqueue */
};

/** skb data structure */
struct ar_skbreq {
    struct list_head list;
    oal_handle *handle;

    // struct urb *urb;
    void* bus_data;             /** ex data for different bus */
    u32 actual_length;          /** ex data len for different bus */

    struct sk_buff *skb;
    u32 type;
    u32 timeout;
    void* priv_data;

    bool is_pwr_msg;
};

/***************************************************************************
 * Function declared
 ***************************************************************************/
/**
 *  @brief This function initializes software
 *
 *  @param handle   A pointer to oal_handle structure
 *
 *  @return         OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_init_sw(IN oal_handle *handle);

/**
 *  @brief Dump the data in skb
 *
 *  @param lvl      Level of the debug, refers to DL_xxx
 *  @param note     Note of the print
 *  @param skb      skb to print
 *  @param len      Length of the data to print
 *
 *  @return         N/A
 */
void oal_dump_skb_data(uint lvl, char *note, struct sk_buff *skb, u32 len);

/**
 *  @brief Deinit the specific skbreq, remove from current list
 *
 *  @param poal_handle   A pointer to oal_handle structure
 *  @param req           skbreq to deinit
 *  @param skb_free      Whether free the skb in skbreq
 *  @param count         Number of the skbreqs in list
 *
 *  @return         N/A
 */
void oal_skbreq_deinit(IN oal_handle *poal_handle, struct ar_skbreq *req, bool skbuff_free, int *counter);

/**
 *  @brief Init all skbreq queues.
 *
 *  @param poal_handle     A pointer to oal_handle structure
 *
 *  @return         N/A
 */
void oal_init_skbreq_queues(IN oal_handle *poal_handle);

/**
 *  @brief Deinit all skbreq queues.
 *
 *  @param poal_handle     A pointer to oal_handle structure
 *
 *  @return         N/A
 */
void oal_deinit_skbreq_queues(IN oal_handle *poal_handle);

/**
 *  @brief Enqueue skbreq at specific type of list tail.
 *
 *  @param poal_handle      A pointer to oal_handle structure
 *  @param req              The skbreq need to add
 *  @param type             Type of the skbreq, refers to SKBREQ_LIST_xxx
 *
 *  @return         N/A
 */
void oal_enqueue_skbreq_list(IN oal_handle *poal_handle, struct ar_skbreq *req, skbreq_list_type type);

/**
 *  @brief Dequeue skbreq from head of specific type of list head.
 *
 *  @param poal_handle      A pointer to oal_handle structure
 *  @param type             Type of the skbreq, refers to SKBREQ_LIST_xxx
 *
 *  @return         The skbreq pop from list if success, otherwise return NULL.
 */
struct ar_skbreq *oal_dequeue_skbreq_list(IN oal_handle *poal_handle, skbreq_list_type type);

/**
 *  @brief Send packet req, dequeue skbreq from list pktout free
 * and push into pktout post. Then queue_work the main work queue.
 *
 *  @param poal_handle      A pointer to oal_handle structure
 *  @param skb              skb with packet data
 *  @param priv_data        Private data for tx done
 *
 *  @return         OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_send_packet_req(IN oal_handle *poal_handle, struct sk_buff *skb, void *priv_data);

/**
 *  @brief Send msg req, dequeue skbreq from list msgout free
 * and push into msgout post. Then queue_work the main work queue.
 *
 *  @param poal_handle      A pointer to oal_handle structure
 *  @param skb              skb with packet data
 *  @param priv_data        Private data for tx done
 *
 *  @return         OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_send_msg_req(IN oal_handle *poal_handle, IN struct sk_buff *skb, IN bool is_pwr_msg);

/**
 *  @brief Send packet req complete, dequeue skbreq from list pktout post
 * and push into pktout free. Then call net device complete.
 *
 *  @param poal_handle      A pointer to oal_handle structure
 *  @param req              skbreq that send complete
 *  @param status           status of last stage
 *
 *  @return         OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_send_packet_complete(IN oal_handle *poal_handle, struct ar_skbreq *req, IN oal_status status);

/**
 *  @brief Send msg req complete, dequeue skbreq from list msgout post
 * and push into msgout free.
 *
 *  @param poal_handle      A pointer to oal_handle structure
 *  @param req              skbreq that send complete
 *  @param status           status of last stage
 *
 *  @return         OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_send_msg_complete(IN oal_handle *poal_handle,
                                   struct ar_skbreq *req,
                                   IN oal_status status);

/**
 *  @brief Judge whether type is msg type.
 *
 *  @param type             Type of the skbreq, refers to SKBREQ_LIST_xxx
 *
 *  @return         Whether type is msg type.
 */
bool is_message_type(u32 type);

/**
 *  @brief Judge whether type is packet type.
 *
 *  @param type             Type of the skbreq, refers to SKBREQ_LIST_xxx
 *
 *  @return         Whether type is packet type.
 */
bool is_packet_type(u32 type);

/**
 *  @brief Suspend the running of the oal handle(TBD)
 *
 *  @param poal_handle      A pointer to oal_handle structure
 *
 *  @return         OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_dev_suspend(IN oal_handle *poal_handle);

/**
 *  @brief Resume the running of the oal handle(TBD)
 *
 *  @param poal_handle      A pointer to oal_handle structure
 *
 *  @return         OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_dev_resume(IN oal_handle *poal_handle);

/**
 *  @brief This function init platform
 *
 *  @param handle   A pointer to oal_handle structure
 *
 *  @return         OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_init_platform(oal_handle *handle);

/**
 *  @brief This function deinit platform
 *
 *  @param handle       A pointer to oal_handle structure
 *
 *  @return             OAL_STATUS_SUCCESS or OAL_STATUS_FAILURE
 */
oal_status oal_deinit_platform(oal_handle *handle);

/**
 * @brief This function adds the card. it will probe the
 *      card, allocate the oal private and initialize the device.
 *
 *  @param card       A pointer to card
 *  @param card_mode  Refers oal_card_mode
 *
 *  @return        A pointer to oal_handle structure
 */
oal_handle *oal_add_card(void *card, struct device *dev, oal_card_mode card_mode, oal_callbacks* cb);

/**
 *  @brief This function removes the card.
 *
 *  @param card    A pointer to card
 *
 *  @return        OAL_STATUS_SUCCESS
 */
oal_status oal_remove_card(void *card);

#endif /* _OAL_MAIN_H */
